﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;      // 可提高可维护性，但可能有性能损耗
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Text;

/*
 * 作者：王付森
 * 日期：2021.12.7
 * 功能：EFEM 协议封装。有效值检查只检查协议中规定的，不检查实际业务中参数有效性。
 * 
 * 说明：不具有部件状态记录功能，如片盒状态、是否经过预对准等
 * 其它：有的类只有一个函数，为什么不直接用委托？      性能
 * 
 * ★状态：ABS CAN 消息的解析在各自的内部类 Analyser 中处理；INF 消息的解析在 BaseGetAnalyser 及子类中处理。
 *       ABS CAN 给 Error 赋值；INF 将消息解析成合适的数据类型给 Result2
 * 
 * 
 * 日期：2021.12.9     初步完成协议封装
 * 日期：2021.12.16    实现2017版协议文件中新增协议
 * 日期：2022.1.6      除了 EVT，其它指令进行关联记录
 * 日期：2022.1.11     添加 Efem.GetCommandState(long)、ExecuteResult、BaseAnalyser.SetResult
 *                    优化 EvtAnalyser
 * 日期：2022.1.17     优化 BaseAnalyser.SetResult，简化子类操作
 *                    Efem 内部类基本完成了协议的解析，ABS 和 CAN 的结论解析只需在 BaseAnalyser 类中添加代码
 *          可维护指数：89
 * 日期：2022.1.18     调整枚举名称、变量名称、注释；修改 BaseAnalyser 定义；ABS、CAN 的结论解析在各自类型的 OnError 函数中实现
 *                    处理多线程中可能出现的 BUG：Efem.GetCommandState 添加 out 返回值，防止状态改变导致异常
 *          可维护指数：92
 * 日期：2022.1.19     Efem 对外接口中的枚举添加有效性检查，优化部分函数的可维护性
 *          可维护指数：91
 * 日期：2022.1.20     根据2020版协议文件进行修改；预留返回消息解析接口
 *          可维护指数：91
 * 日期：2022.1.22     调整结果解析相关代码，便于扩展
 *          可维护指数：91
 * 日期：2022.1.23     实现大部分消息的结果解析
 *          可维护指数：90
 * 日期：2022.2.14     修复 SET:SIZE 消息链死等的情况：SIZE 消息只到 ACK，无 INF
 * 日期：2022.2.16     修复多线程数据安全问题
 * 日期：2022.2.17     修复 CAN ABS NAK 消息解析异常（无法找到对应的命令并设置状态）；给消息链添加时间戳；
 *                    部分指令到 ACK 结束，修改处理方式，其中 TransReq、MODE 待验证
 *          可维护指数：90
 * 日期：2022.2.25     添加 Speed 接口，用于设置/获取 Robot 速度
 * 
 */

namespace ConsoleTest
{
    class Efem
    {
        TcpClient client;
        ConcurrentQueue<SocketParam> buffer;
        List<CommandState> commands;
        Dictionary<int, BaseAnalyser> analysers;

        volatile bool flag_run;  // thread 运行标记
        System.Threading.Thread thread;
        bool flag_ems;

        public Efem()
        {
            this.analysers = new Dictionary<int, BaseAnalyser>()
            {
                {"INF".GetHashCode(), new InfAnalyser() },
                {"ABS".GetHashCode(), new AbsAnalyser() },
                {"EVT".GetHashCode(), new EvtAnalyser(this) },
                {"ACK".GetHashCode(), new AckAnalyser() },
                {"NAK".GetHashCode(), new NakAnalyser() },
                {"CAN".GetHashCode(), new CanAnalyser() },
            };
            this.commands = new List<CommandState>(8);
        }

        public void Connect(string ip, ushort port)
        {
            this.client = new TcpClient();
            try
            {
                this.client.Connect(System.Net.IPAddress.Parse(ip), port);
            }
            catch
            {
                this.client = null;
                throw;
            }

            this.Preparing();
        }

        public void Disconnect()
        {
            if (this.client == null)
            {
                return;
            }

            this.flag_run = false;
            (this.client as IDisposable).Dispose();
            this.client = null;
            this.thread.Join();
        }

        #region 对外接口

        /// <summary>
        /// 初始化 load port、robot、align
        /// </summary>
        public ExecuteResult Init4All()
        {
            this.flag_ems = false;
            return this.SendCommand("MOV", "ALL");
        }

        /// <summary>
        /// 初始化 load port
        /// </summary>
        /// <param name="index">哪个 LP，从 1 开始</param>
        public ExecuteResult Init4LoadPort(byte index) => this.SendCommand("MOV", "P" + index);

        /// <summary>
        /// 初始化 robot
        /// </summary>
        /// <param name="index">哪个 Robot，从 1 开始</param>
        public ExecuteResult Init4Robot(byte index) => this.SendCommand("MOV", "ROBOT" + index);

        /// <summary>
        /// 初始化 align
        /// </summary>
        /// <param name="index">哪个 Aligner，从 1 开始</param>
        public ExecuteResult Init4Align(byte index) => this.SendCommand("MOV", "ALIGN" + index);

        /// <summary>
        /// 检测 load port、robot、aligner 开始位置
        /// </summary>
        public ExecuteResult Orgsh4All() => this.SendCommand("MOV", "ALL");

        /// <summary>
        /// 检测 load port 开始位置
        /// </summary>
        /// <param name="index">哪个 LP，从 1 开始</param>
        public ExecuteResult Orgsh4LoadPort(byte index) => this.SendCommand("MOV", "P" + index);

        /// <summary>
        /// 检测 robot 开始位置
        /// </summary>
        /// <param name="index">哪个 Robot，从 1 开始</param>
        public ExecuteResult Orgsh4Robot(byte index) => this.SendCommand("MOV", "ROBOT" + index);

        /// <summary>
        /// 检测 aligner 开始位置
        /// </summary>
        /// <param name="index">哪个 Aligner，从 1 开始</param>
        public ExecuteResult Orgsh4Align(byte index) => this.SendCommand("MOV", "ALIGN" + index);

        /// <summary>
        /// 设置 Robot 速度
        /// </summary>
        /// <param name="index">哪个 Robot，从 1 开始</param>
        /// <param name="percent">百分比</param>
        public ExecuteResult Speed(byte index, byte percent)
        {
            if (percent < 1)
            {
                percent = 1;
            }
            else if (percent > 100)
            {
                percent = 100;
            }
            return this.SendCommand("SET", $"ROBOT{index}/{percent}");
        }

        /// <summary>
        /// 获取 Robot 速度
        /// </summary>
        /// <param name="index">哪个 Robot，从 1 开始</param>
        public ExecuteResult Speed4Get(byte index) => this.SendCommand("GET", $"ROBOT{index}");

        /// <summary>
        /// 锁定 load port
        /// </summary>
        /// <param name="index">哪个 LP，从 1 开始</param>
        public ExecuteResult Lock(byte index) => this.SendCommand("MOV", "P" + index);

        /// <summary>
        /// 解锁 load port
        /// </summary>
        /// <param name="index">哪个 LP，从 1 开始</param>
        public ExecuteResult Unlock(byte index) => this.SendCommand("MOV", "P" + index);

        /// <summary>
        /// 移动 load port 到停靠点
        /// </summary>
        /// <param name="index">哪个 LP，从 1 开始</param>
        public ExecuteResult Dock(byte index) => this.SendCommand("MOV", "P" + index);

        /// <summary>
        /// 将 load port 移出停靠点
        /// </summary>
        /// <param name="index">哪个 LP，从 1 开始</param>
        public ExecuteResult Undock(byte index) => this.SendCommand("MOV", "P" + index);

        /// <summary>
        /// 打开 load port
        /// </summary>
        /// <param name="index">哪个 LP，从 1 开始</param>
        public ExecuteResult Open(byte index) => this.SendCommand("MOV", "P" + index);

        /// <summary>
        /// 关闭 load port
        /// </summary>
        /// <param name="index">哪个 LP，从 1 开始</param>
        public ExecuteResult Close(byte index) => this.SendCommand("MOV", "P" + index);

        /// <summary>
        /// 扫描片盒
        /// </summary>
        /// <param name="index">哪个 LP，从 1 开始</param>
        public ExecuteResult Wafsh4LoadPort(byte index) => this.SendCommand("MOV", "P" + index);

        /// <summary>
        /// 扫描片盒
        /// </summary>
        /// <param name="index">哪个 Buffer Port，从 1 开始</param>
        [Obsolete("设备可能不支持")]
        public ExecuteResult Wafsh4BufferPort(byte index) => this.SendCommand("MOV", "BF" + index);

        /// <summary>
        /// 获取晶圆情况。哪些位置有片。
        /// </summary>
        /// <param name="index">哪个 LP，从 1 开始</param>
        public ExecuteResult MapDT4LoadPort(byte index) => this.SendCommand("GET", "P" + index, new GetMapdtAnalyser());

        /// <summary>
        /// 获取晶圆情况。哪些位置有片。
        /// </summary>
        /// <param name="index">哪个 Buffer Port，从 1 开始</param>
        [Obsolete("设备可能不支持")]
        public ExecuteResult MapDT4BufferPort(byte index) => this.SendCommand("GET", "BF" + index, new GetMapdtAnalyser());

        /// <summary>
        /// 将 robot 移动到指定工位前
        /// </summary>
        /// <param name="stage">移动到哪个工位</param>
        /// <param name="index">工位序号，从 1 开始</param>
        /// <param name="slot">仅对 load port 有效，从 1 开始</param>
        /// <param name="arm">要使用的机械臂</param>
        /// <param name="pos">上还是下。上为放片位置，下为取片位置</param>
        public ExecuteResult Goto(EfemStages stage, byte index, byte slot, Arms arm, HeightPosition pos)
        {
            // 枚举校验，防止使用强转
            CheckUtil.CheckEnum(ref stage);
            CheckUtil.CheckEnum(ref arm);
            CheckUtil.CheckEnum(ref pos);

            string p1 = this.GetHorPosParam(ref stage, ref index, ref slot);
            return this.SendCommand("MOV", string.Join("/", p1, arm, pos));
        }

        /// <summary>
        /// 从指定工位取片
        /// </summary>
        /// <param name="stage">取片工位</param>
        /// <param name="index">工位序号，从 1 开始</param>
        /// <param name="slot">仅对 load port 有效，从 1 开始</param>
        /// <param name="arm">要使用的机械臂</param>
        public ExecuteResult Load(EfemStages stage, byte index, byte slot, Arms arm)
        {
            // 枚举校验，防止使用强转
            CheckUtil.CheckEnum(ref stage);
            CheckUtil.CheckEnum(ref arm);

            string p1 = this.GetHorPosParam(ref stage, ref index, ref slot);
            return this.SendCommand("MOV", string.Join("/", p1, arm));
        }

        /// <summary>
        /// 将片放到指定工位
        /// </summary>
        /// <param name="stage">放片工位</param>
        /// <param name="index">工位序号，从 1 开始</param>
        /// <param name="slot">仅对 load port 有效，从 1 开始</param>
        /// <param name="arm">要使用的机械臂</param>
        public ExecuteResult Unload(EfemStages stage, byte index, byte slot, Arms arm)
        {
            // 枚举校验，防止使用强转
            CheckUtil.CheckEnum(ref stage);
            CheckUtil.CheckEnum(ref arm);

            string p1 = this.GetHorPosParam(ref stage, ref index, ref slot);
            return this.SendCommand("MOV", string.Join("/", p1, arm));
        }

        /// <summary>
        /// 将片从某个工位移动到另一个工位。一般 <paramref name="arm_out"/> 与 <paramref name="arm_in"/> 是同一个。
        /// </summary>
        /// <param name="src">从哪个工位取片</param>
        /// <param name="index_src">取片工位的序号，从 1 开始</param>
        /// <param name="slot_src">仅对 load port 有效，从 1 开始</param>
        /// <param name="arm_out">取片时使用的机械臂</param>
        /// <param name="des">片放到哪个工位</param>
        /// <param name="index_des">放片工位的序号，从 1 开始</param>
        /// <param name="slot_des">仅对 load port 有效，从 1 开始</param>
        /// <param name="arm_in">放片时使用的机械臂</param>
        public ExecuteResult Trans(EfemStages src, byte index_src, byte slot_src, Arms arm_out,
                          EfemStages des, byte index_des, byte slot_des, Arms arm_in)
        {
            // 枚举校验，防止使用强转
            CheckUtil.CheckEnum(ref src);
            CheckUtil.CheckEnum(ref arm_out);
            CheckUtil.CheckEnum(ref des);
            CheckUtil.CheckEnum(ref arm_in);

            string p1 = this.GetHorPosParam(ref src, ref index_src, ref slot_src);
            string p4 = this.GetHorPosParam(ref des, ref index_des, ref slot_des);
            return this.SendCommand("MOV", $"{p1}>{arm_out}/{arm_in}>{p4}");
        }

        /// <summary>
        /// 交换指定工位的晶片
        /// </summary>
        /// <param name="stage">交换哪个工位的片</param>
        /// <param name="index">工位的序号，从 1 开始</param>
        /// <param name="slot">仅对 load port 有效，从 1 开始</param>
        /// <param name="arm_out">取片时用的机械臂。ARM1 为高位机械臂，ARM2 为低位机械臂</param>
        /// <param name="arm_in">放片时用的机械臂。ARM1 为高位机械臂，ARM2 为低位机械臂</param>
        public ExecuteResult Change(EfemStages stage, byte index, byte slot, Arms arm_out, Arms arm_in)
        {
            // 枚举校验，防止使用强转
            CheckUtil.CheckEnumFlag(ref stage, EfemStages.LoadPort | EfemStages.BufferPort);
            CheckUtil.CheckEnum(ref arm_out);
            CheckUtil.CheckEnum(ref arm_in);

            string p1 = this.GetHorPosParam(ref stage, ref index, ref slot);
            return this.SendCommand("MOV", string.Join("/", p1, arm_out, arm_in));
        }

        /// <summary>
        /// 根据 load port 进行校准
        /// </summary>
        /// <param name="alignNo">校准器序号，从 1 开始</param>
        /// <param name="portNo">port 号，从 1 开始</param>
        public ExecuteResult Align4LoadPort(byte alignNo, byte portNo)
        {
            return this.SendCommand("SET", string.Join("/", "ALIGN" + alignNo, "P" + portNo), null, true);
        }

        /// <summary>
        /// 根据 buffer port 进行校准
        /// </summary>
        /// <param name="alignNo">校准器序号，从 1 开始</param>
        /// <param name="portNo">port 号，从 1 开始</param>
        [Obsolete("设备可能不支持")]
        public ExecuteResult Align4BufferPort(byte alignNo, byte portNo)
        {
            return this.SendCommand("SET", string.Join("/", "ALIGN" + alignNo, "BF" + portNo), null, true);
        }

        /// <summary>
        /// 根据 load lock 进行校准
        /// </summary>
        /// <param name="alignNo">校准器序号，从 1 开始</param>
        /// <param name="ll">load lock</param>
        public ExecuteResult Align4LoadLock(byte alignNo, Stages ll)
        {
            // 枚举校验，防止使用强转
            CheckUtil.CheckEnum(ref ll);

            return this.SendCommand("SET", string.Join("/", "ALIGN" + alignNo, ll), null, true);
        }

        /// <summary>
        /// 根据角度进行校准
        /// </summary>
        /// <param name="alignNo">校准器序号，从 1 开始</param></param>
        /// <param name="angle">要校准的角度，单位：°</param>
        public ExecuteResult Align4Angle(byte alignNo, float angle)
        {
            while (angle < 0)
            {
                angle += 360f;
            }
            angle %= 360;
            return this.SendCommand("SET", string.Join("/", "ALIGN" + alignNo, $"D{angle:f2}"), null, true);
        }

        /// <summary>
        /// 开始校准
        /// </summary>
        /// <param name="alignNo">校准器序号，从 1 开始</param></param>
        public ExecuteResult Align(byte alignNo) => this.SendCommand("MOV", "ALIGN" + alignNo);

        /// <summary>
        /// 校准基板并读取晶圆 ID
        /// </summary>
        /// <param name="align">aligner number</param>
        /// <param name="id">0-99 检测大版背面，100-199 检测小版背面，200-299 检测大版正面，300-399 检测小版正面</param>
        /// <param name="motion">是否进行预对准</param>
        public ExecuteResult Wid(byte align, ushort id, bool motion)
        {
            // MOV:WID/ALIGN1/WIDRecipeXXX[/1|0]
            // WIDRecipe000-WIDRecipe099    大版背面
            // WIDRecipe100-WIDRecipe199    小版背面
            // WIDRecipe200-WIDRecipe299    大版正面
            // WIDRecipe300-WIDRecipe399    小版正面
            return this.SendCommand("MOV", $"ALIGN{align}/WIDRecipe{id:d3}/{(motion ? "1" : "0")}", new GetWidAnalyser());
        }

        /// <summary>
        /// 复位 robot
        /// </summary>
        /// <param name="index">序号，从 1 开始</param>
        public ExecuteResult Home4Robot(byte index) => this.SendCommand("MOV", "ROBOT" + index);

        /// <summary>
        /// 复位 load port
        /// </summary>
        /// <param name="index">序号，从 1 开始</param>
        public ExecuteResult Home4LoadPort(byte index) => this.SendCommand("MOV", "P" + index);

        /// <summary>
        /// 暂停运作
        /// </summary>
        public ExecuteResult Hold() => this.SendCommand("MOV", null);

        /// <summary>
        /// 恢复运作
        /// </summary>
        public ExecuteResult Restr() => this.SendCommand("MOV", null, null, true);

        /// <summary>
        /// 中止运作
        /// </summary>
        public ExecuteResult Abort() => this.SendCommand("MOV", null);

        /// <summary>
        /// 急停。急停后需要重新初始化
        /// </summary>
        public ExecuteResult Ems() => this.SendCommand("MOV", null);

        /// <summary>
        /// 设置 ARM1/ARM2 的夹持状态
        /// </summary>
        /// <param name="arm">哪个机械臂，只能是 ARM1/ARM2</param>
        /// <param name="ss">状态</param>
        public ExecuteResult Clamp4Arm(Arms arm, SwitchState ss)
        {
            // 枚举校验，防止使用强转
            CheckUtil.CheckEnum(ref arm);
            CheckUtil.CheckEnum(ref ss);

            return this.SendCommand("SET", $"{arm}/{ss}", null, true);
        }

        /// <summary>
        /// 获取 ARM1/ARM2 的夹持状态
        /// </summary>
        /// <param name="arm">哪个机械臂，只能是 ARM1/ARM2</param>
        public ExecuteResult Clamp4Arm4Get(Arms arm)
        {
            // 枚举校验，防止使用强转
            CheckUtil.CheckEnum(ref arm);

            return this.SendCommand("GET", $"{arm}");
        }

        /// <summary>
        /// 设置 aligner 的夹持状态
        /// </summary>
        /// <param name="index">aligner 号，从 1 开始</param>
        /// <param name="ss">状态</param>
        public ExecuteResult Clamp4Align(byte index, SwitchState ss)
        {
            // 枚举校验，防止使用强转
            CheckUtil.CheckEnum(ref ss);

            return this.SendCommand("SET", $"ALIGN{index}/{ss}", null, true);
        }

        /// <summary>
        /// 获取 aligner 的夹持状态
        /// </summary>
        /// <param name="index">aligner 号，从 1 开始</param>
        public ExecuteResult Clamp4Align4Get(byte index) => this.SendCommand("GET", $"ALIGN{index}");

        /// <summary>
        /// 获取 EFEM 盘片情况
        /// </summary>
        /// <returns></returns>
        public ExecuteResult State4Track() => this.SendCommand("GET", "TRACK", new GetTrackAnalyser());

        /// <summary>
        /// 获取气压传感器组的气压
        /// </summary>
        /// <param name="index">哪个组，从 1 开始</param>
        public ExecuteResult State4Pressure(byte index) => this.SendCommand("GET", $"PRS{index}", new GetSensorAnalyser());

        /// <summary>
        /// 获取风扇转速
        /// </summary>
        /// <param name="index">风扇组，从 1 开始</param>
        public ExecuteResult State4FanRotation(byte index) => this.SendCommand("GET", $"FFU{index}", new GetSensorAnalyser());

        /// <summary>
        /// 获取 Aligner 坐标轴信息
        /// </summary>
        public ExecuteResult State4Aligner() => this.SendCommand("GET", "ALIGNMENT", new GetAlignmentAnalyser());


        /// <summary>
        /// 设置全部 load port E84工作模式
        /// </summary>
        /// <param name="mode">E84工作模式</param>
        public ExecuteResult Mode4All(E84Mode mode)
        {
            // 枚举校验，防止使用强转
            CheckUtil.CheckEnum(ref mode);

            return this.SendCommand("SET", $"ALL/{mode}", null, true);
        }

        /// <summary>
        /// 设置单个 load port E84工作模式
        /// </summary>
        /// <param name="index">序号，从 1 开始</param>
        /// <param name="mode">E84工作模式</param>
        public ExecuteResult Mode4LoadPort(byte index, E84Mode mode)
        {
            // 枚举校验，防止使用强转
            CheckUtil.CheckEnum(ref mode);

            return this.SendCommand("SET", $"P{index}/{mode}", null, true);
        }

        /// <summary>
        /// 获取 load port 的E84工作模式
        /// </summary>
        /// <param name="index">序号，从 1 开始</param>
        public ExecuteResult Mode4Get(byte index) => this.SendCommand("GET", "P" + index);

        /// <summary>
        /// 请求 load port 哪个步骤按E84自动运作
        /// </summary>
        /// <param name="index">序号，从 1 开始</param>
        /// <param name="type">自动运作的步骤</param>
        public ExecuteResult TransReq(byte index, RequestType type)
        {
            // 枚举校验，防止使用强转
            CheckUtil.CheckEnum(ref type);

            return this.SendCommand("MOV", $"P{index}/{type}", null, true);
        }

        /// <summary>
        /// 获取 load port E84模式
        /// </summary>
        /// <param name="index">序号，从 1 开始</param>
        public ExecuteResult TransReq4Get(byte index) => this.SendCommand("GET", $"P{index}");

        /// <summary>
        /// 设置信号塔信号状态
        /// </summary>
        /// <param name="tower">信号塔信号类型</param>
        /// <param name="status">状态</param>
        /// <param name="time">闪烁时间，单位：100ms</param>
        public ExecuteResult SigOut4Tower(Towers tower, LightStatus status, byte time)
        {
            // 枚举校验，防止使用强转
            CheckUtil.CheckEnum(ref tower);
            CheckUtil.CheckEnum(ref status);

            string p3;
            switch (tower)
            {
                case Towers.BUZZER1:
                case Towers.BUZZER2:
                    p3 = $"{(status == LightStatus.BLINK ? "ON" : status.ToString())}";
                    break;
                default:
                    p3 = this.GetLightString(ref status, ref time);
                    break;
            }
            return this.SendCommand("SET", $"STOWER/{tower}/{p3}");
        }

        /// <summary>
        /// 设置 load port 指示灯状态
        /// </summary>
        /// <param name="index">序号，从 1 开始</param>
        /// <param name="indicator">指示灯</param>
        /// <param name="status">状态</param>
        /// <param name="time">闪烁时间，单位：100ms</param>
        public ExecuteResult SigOut4LoadPort(byte index, Indicator indicator, LightStatus status, byte time)
        {
            // 枚举校验，防止使用强转
            CheckUtil.CheckEnum(ref indicator);
            CheckUtil.CheckEnum(ref status);

            return this.SendCommand("SET", $"P{index}/{indicator}/{this.GetLightString(ref status, ref time)}");
        }

        /// <summary>
        /// 获取 IO 状态
        /// </summary>
        /// <param name="port">true - 获取 load port 的 IO 状态，否则获取系统 IO 状态</param>
        /// <param name="index">load port 序号，从 1 开始</param>
        public ExecuteResult SigStat(bool port, byte index)
        {
            return this.SendCommand("GET", port ? "P" + index : "SYSTEM", new GetSigStateAnalyser());
        }

        /// <summary>
        /// 打开/关闭事件通知
        /// </summary>
        /// <param name="type">事件类型</param>
        /// <param name="state">打开/关闭</param>
        public ExecuteResult Event(EventTypes type, SwitchState state)
        {
            // 枚举校验，防止使用强转
            CheckUtil.CheckEnum(ref type);
            CheckUtil.CheckEnum(ref state);

            return this.SendCommand("SET", $"{type}/{state}", null, true);
        }

        /// <summary>
        /// 获取事件通知设置
        /// </summary>
        /// <param name="type">事件类型</param>
        public ExecuteResult Event4Get(EventTypes type)
        {
            // 枚举校验，防止使用强转
            CheckUtil.CheckEnumNotNone(ref type);

            return this.SendCommand("GET", $"{type}");
        }

        /// <summary>
        /// 设置片盒 ID
        /// </summary>
        /// <param name="index">load port 序号，从 1 开始</param>
        /// <param name="id">carrier ID</param>
        public ExecuteResult CSTID(byte index, string id) => this.SendCommand("SET", $"P{index}/{id}");

        /// <summary>
        /// 获取片盒 ID
        /// </summary>
        /// <param name="index">load port 序号，从 1 开始</param>
        public ExecuteResult CSTID4Get(byte index) => this.SendCommand("GET", $"P{index}");

        /// <summary>
        /// 启用/停用 load port N2 净化功能
        /// </summary>
        /// <param name="index">load port 序号，从 1 开始</param>
        /// <param name="enabled">启用/停用</param>
        [Obsolete("设备不一定支持")]
        public ExecuteResult Purge4Set(byte index, bool enabled) => this.SendCommand("SET", $"P{index}/{(enabled ? "ON" : "OFF")}");

        /// <summary>
        /// 获取 load port N2 净化功能状态
        /// </summary>
        /// <param name="index">load port 序号，从 1 开始</param>
        [Obsolete("设备不一定支持")]
        public ExecuteResult Purge4Get(byte index) => this.SendCommand("GET", $"P{index}");

        /// <summary>
        /// 启用/停用 load port N2 净化功能
        /// </summary>
        /// <param name="index">load port 序号，从 1 开始</param>
        /// <param name="enabled">启用/停用</param>
        [Obsolete("设备不一定支持")]
        public ExecuteResult Purge(byte index, bool enabled) => this.SendCommand("MOV", $"P{index}/{(enabled ? "ON" : "OFF")}");

        /// <summary>
        /// 设置指定目标上晶片的尺寸
        /// </summary>
        /// <param name="stage">目标类型</param>
        /// <param name="index">目标序号，从 1 开始</param>
        /// <param name="slot">仅对 load port 有效，从 1 开始</param>
        /// <param name="size">true - 表示 200mm，否则表示 300mm</param>
        public ExecuteResult Size(EfemStages stage, byte index, byte slot, SizeMode size)
        {
            // 枚举校验，防止使用强转
            CheckUtil.CheckEnum(ref stage);
            CheckUtil.CheckEnumFlag(ref size, SizeMode.SNONE);

            string p1 = this.GetHorPosParam(ref stage, ref index, ref slot);
            return this.SendCommand("SET", $"{p1}/{size.ToString().Substring(1)}", null, true);
        }

        /// <summary>
        /// 获取指定目标设置的晶片尺寸
        /// </summary>
        /// <param name="stage">目标类型</param>
        /// <param name="index">目标序号，从 1 开始</param>
        /// <param name="slot">仅对 load port 有效，从 1 开始</param>
        public ExecuteResult Size4Get(EfemStages stage, byte index, byte slot)
        {
            // 枚举校验，防止使用强转
            CheckUtil.CheckEnum(ref stage);

            string p1 = this.GetHorPosParam(ref stage, ref index, ref slot);
            return this.SendCommand("GET", p1);
        }

        /// <summary>
        /// 设置机械臂上晶片尺寸
        /// </summary>
        /// <param name="arm">ARM1（高位）/ARM2（低位）</param>
        /// <param name="size">true - 表示 200mm，否则表示 300mm</param>
        public ExecuteResult Size4Arm(Arms arm, SizeMode size)
        {
            // 枚举校验，防止使用强转
            CheckUtil.CheckEnum(ref arm);
            CheckUtil.CheckEnumFlag(ref size, SizeMode.SNONE);

            return this.SendCommand("SET", $"{arm}/{size.ToString().Substring(1)}", null, true);
        }

        /// <summary>
        /// 获取机械臂设置的晶片尺寸
        /// </summary>
        /// <param name="arm">ARM1（高位）/ARM2（低位）</param>
        public ExecuteResult Size4Arm4Get(Arms arm)
        {
            // 枚举校验，防止使用强转
            CheckUtil.CheckEnum(ref arm);

            return this.SendCommand("GET", arm.ToString());
        }

        /// <summary>
        /// 锁定指定 load port 的适配器
        /// </summary>
        /// <param name="index">序号，从 1 开始</param>
        public ExecuteResult AdpLock(byte index) => this.SendCommand("MOV", "P" + index);

        /// <summary>
        /// 解除指定 load port 适配器的锁定
        /// </summary>
        /// <param name="index">序号，从 1 开始</param>
        public ExecuteResult AdpUnlock(byte index) => this.SendCommand("MOV", "P" + index);

        public ExecuteResult Error4Get() => this.SendCommand("GET", null, new GetErrorAnalyser());

        public ExecuteResult Error4Clear() => this.SendCommand("SET", "CLEAR", null, true);

        #endregion

        public ExecuteResult GetVersion() => this.SendCommand("GET", "VER", null, false, "STATE");

        //public ExecuteResult SendCustomCommand(byte type, string msg, string param, bool ackSucced)
        //{
        //    string[] mts = { "MOV", "GET", "SET" };
        //    type %= 3;
        //    return this.SendCommand(mts[type], param, null, ackSucced, msg);
        //}

        #region  通讯处理

        void Preparing()
        {
            this.buffer = new ConcurrentQueue<SocketParam>();
            this.commands.Clear();

            SocketParam sp = new SocketParam();
            this.client.Client.BeginReceive(sp.Buffer, 0, sp.Buffer.Length, SocketFlags.None, this.TcpCallback, sp);
            this.flag_run = true;
            (this.thread = new System.Threading.Thread(this.BufferAnalyser) { IsBackground = true }).Start();
        }

        // TCP 数据交互
        void TcpCallback(IAsyncResult ia)
        {
            //if (this.client == null)
            //{
            //    return;
            //}
            SocketParam sp = ia.AsyncState as SocketParam;
            SocketError se;
            try
            {
                sp.Count = this.client.Client.EndReceive(ia, out se);
            }
            catch
            {
                // 有概率在关闭后触发空引用异常，无需处理
                return;
            }
            if (se != SocketError.Success)
            {
                this.client = null;
                this.OnCommunicationError();
                return;
            }

            sp.Timestamp = DateTime.Now;
            this.buffer.Enqueue(sp);
            sp = new SocketParam();

            this.client.Client.BeginReceive(sp.Buffer, 0, sp.Buffer.Length, SocketFlags.None, this.TcpCallback, sp);
        }

        void BufferAnalyser()
        {
            while (this.flag_run)
            {
                if (!this.buffer.TryDequeue(out SocketParam tmp))
                {
                    System.Threading.Thread.Sleep(5);
                    continue;
                }

                this.AnalysisResponse(tmp);
            }
        }
        void AnalysisResponse(SocketParam sp)
        {
            string resp = Encoding.ASCII.GetString(sp.Buffer, 0, sp.Count);
            int idx = resp.LastIndexOf(';');
            resp = resp.Substring(0, idx);

            BaseAnalyser ba;
            try
            {
                ba = this.analysers[resp.Substring(0, 3).GetHashCode()];
                ba.Analysis(this, resp.Substring(4), ref sp.Timestamp);
            }
            catch
            {
                this.OnUnhandledResponse(resp);
            }
        }

        // 对外公布的函数发送消息
        ExecuteResult SendCommand(string type, string arg, BaseGetAnalyser analyser = null, bool ackSucced = false,
            [CallerMemberName] string msg = null)
        {
            CheckUtil.CheckTcpClient(this.client);
            int idx = msg.IndexOf('4');
            if (idx > 0) msg = msg.Substring(0, idx);
            string msg_para = $"{msg.ToUpper()}{(string.IsNullOrEmpty(arg) ? "" : $"/{arg}")}";
            return new ExecuteResult(this, this.SendMessage(type, msg_para, analyser, ackSucced));
        }

        // 给 EFEM 直接发送消息,对外接口不调用
        long SendMessage(string type, string msg_para, BaseGetAnalyser analyser, bool ackSucced)
        {
            if (this.SyncExist(type, msg_para))
            {
                throw new InvalidOperationException();
            }

            long tag = DateTime.Now.Ticks;
            this.SyncAdd(new CommandState(tag, type, msg_para, analyser ?? new BaseGetAnalyser(), ackSucced));

            string cmd = string.Concat(type, ":", msg_para, ";\r");
            this.client.Client.Send(Encoding.ASCII.GetBytes(cmd));

            return tag;
        }

        // 内部类自动响应 EFEM 的回复
        void InnerSendMessage(string type, string msg_para)
        {
            CheckUtil.CheckTcpClient(this.client);

            string cmd = string.Concat(type, ":", msg_para, ";\r");
            this.client.Client.Send(Encoding.ASCII.GetBytes(cmd));
        }

        #endregion

        #region 私有函数

        string GetHorPosParam(ref EfemStages stage, ref byte index, ref byte slot)
        {
            switch (stage)
            {
                case EfemStages.LoadPort:
                    return $"P{index}{slot:d2}";
                case EfemStages.Align:
                    return "ALIGN" + index;
                case EfemStages.BufferPort:
                    return $"BF{index}{slot:d2}";
                case EfemStages.Stage:
                default:    // 已经校验过，实际不会触发 default
                    return $"{(Stages)(index - 1)}{slot:d2}";
            }
        }

        string GetLightString(ref LightStatus status, ref byte time)
        {
            string p3 = status.ToString();
            if (status == LightStatus.BLINK && (time > 0 && time < 201))
            {
                p3 += time;
            }
            return p3;
        }

        #region 2022.2.16

        [MethodImpl(MethodImplOptions.Synchronized)]
        CommandState SyncFindByTag(long tag) => this.commands.FirstOrDefault(i => i.Tag == tag);
        [MethodImpl(MethodImplOptions.Synchronized)]
        CommandState SyncFindByContent(Func<CommandState, bool> func) => this.commands.FirstOrDefault(func);
        [MethodImpl(MethodImplOptions.Synchronized)]
        bool SyncExist(string type, string msg_param) => this.commands.Exists(i => i.Type == type && i.MsgParam == msg_param);
        [MethodImpl(MethodImplOptions.Synchronized)]
        void SyncAdd(CommandState cmd) => this.commands.Add(cmd);
        [MethodImpl(MethodImplOptions.Synchronized)]
        void SyncRemove(CommandState cmd) => this.commands.Remove(cmd);

        #endregion

        #endregion

        internal CommandState GetCommandState(long tag, out CommandStatus state)
        {
            CommandState cs = this.SyncFindByTag(tag);
            //if (cs == null)
            //{
            //    // 不应该出现
            //    throw new ArgumentException();
            //}

            state = cs.State;
            if (state != CommandStatus.Executing)
            {
                this.SyncRemove(cs);
            }
            else if (this.flag_ems)
            {
                // 这个判断不可以移动到上一个判断之前，防止指令明明执行完了，状态却被改成 EMS

                // 不然急停后一直在集合中
                state = cs.State = CommandStatus.EMS;
                this.SyncRemove(cs);
            }
            return cs;
        }

        ~Efem() => this.Disconnect();

        #region 事件触发函数

        void OnMapdtReaded(string lpOrbf, string data)
        {
            // lp eg. P1、P2 或者 BF1、BF2
            // data eg.：xxxxxxxxxxxxxxxxxxxxxxxx
            // 0-No substrate
            // 1-Normal substrate
            // 2-Abnormal-thickness substrate (thick substrate)
            // 3-Cross-slotted substrate
            // 4-Bow substrate
            // 7-Multiple substrates (two or more substrates in the same slot)
            // 8-Abnormal-thickness substrate (thin substrate)
            // 9-Others

            //GetMapdtAnalyser.ConvertFromString(data);
        }
        void OnVersionReceived(string ver)
        {
            // eg. 1.1.1.1(2020-12-22)
        }
        void OnTrackStateReaded(string data)
        {
            // eg. 支持混合尺寸情况
            // NONE/????            无片；独臂
            // NONE/NONE            上下臂无片；无 Aligner
            // 300/NONE/NONE        上臂300的片，下臂无片，Aligner1 无片；一个 Aligner
            // 300/200/150/NONE     上臂300的片，下臂200片，Aligner1 150片，Aligner2 无片

            // eg. 支持单尺寸情况
            // 0?                   无片；独臂
            // 00                   上下臂无片；无 Aligner
            // 100                  上臂有片；下臂无片，Aligner1 无片；一个 Aligner
            // 1110                 上臂有片；下臂有片，Aligner1 有片，Aligner2 无片

            //GetTrackAnalyser.ConvertFromString(data);
        }
        void OnPressureReaded(string device, string data)
        {
            // device eg. DPRS1、DPRS2...
            // data eg.: SNO1|xxxxxxxx,SNO2|xxxxxxxx,.....  传感器的气压值 mPa，

            //GetSensorAnalyser.ConvertFromString(data);
        }
        void OnFanSpeedReaded(string fan, string data)
        {
            // fan eg. FFU1、FFU2
            // data eg. FNO1|xxxxxxxx,FNO2|xxxxxxxx         风扇的转速 rpm

            //GetSensorAnalyser.ConvertFromString(data);
        }
        void OnAlignmengChanged(string aligner, string data)
        {
            // aligner 暂时无用，对多个 Aligner 设备可能有用
            // data eg. R,X,Y
            // R- R轴角度，0.001°
            // X- X轴位置，μm
            // Y- Y轴位置，μm

            //GetAlignmentAnalyser.ConvertFromString(data);
        }
        void OnCarrierIdReaded(string lp, string id)
        {
        }
        void OnWidRecipeReaded(string aligner, string type, string data)
        {
            //EVT:WID/ALIGN1/WIDRecipe100/READFAIL,0,".\ImageLog\20210604153226_406_001__NG.bmp";
            //EVT:WID/ALIGN1/WIDRecipe100/READFAIL;

            //GetWidAnalyser.ConvertFromString(data);
        }
        void OnRequestChanged(string lp, RequestType request)
        {
        }
        void OnSystemIOChanged(string rsv, int input, int output)
        {
            //Console.WriteLine($"系统 input IO 状态改变：{(SystemInputIO)input}");
            //Console.WriteLine($"系统 output IO 状态改变：{(SystemOutputIO)output}");
        }
        void OnLoadPortIOChanged(string ll, int input, int output)
        {
            //Console.WriteLine($"load port input IO 状态改变：{(LoadPortInputIO)input}");
            //Console.WriteLine($"load port output IO 状态改变：{(LoadPortOutputIO)output}");
        }
        void OnCommunicationError()
        {
            for (int i = this.commands.Count - 1; i > -1; i--)
            {
                this.commands[i].State = CommandStatus.TcpError;
            }
        }

        void OnLogUnknownKeyword(string msgFormat, [CallerMemberName] string method = null, [CallerLineNumber] int line = 0)
        {
            // 没有实现的，或者未知的信息，通过该函数记录到日志中
            string msg = string.Format(msgFormat, method, line);
        }

        void OnUnhandledResponse(string msg_param)
        {
        }

        #endregion

        #region 内部类

        class SocketParam
        {
            public int Count;
            public byte[] Buffer;
            public DateTime Timestamp;

            public SocketParam() => this.Buffer = new byte[512];
        }

        abstract class BaseAnalyser
        {
            protected string msgParam;        // 给函数 Contains 使用

            /// <summary>
            /// 分析收到的回复
            /// </summary>
            /// <param name="efem">Efem 实例</param>
            /// <param name="msg_param">消息和参数,不带分号和结束符(;\r)</param>
            public virtual void Analysis(Efem efem, string msg_param, ref DateTime timestamp)
            {
                this.msgParam = msg_param;
                this.SetResult(efem, msg_param, CommandStatus.Executing, ref timestamp);
            }

            protected void SetResult(Efem efem, string msg_param, CommandStatus status, ref DateTime timestamp)
            {
                CommandState cmd = efem.SyncFindByContent(this.Contains);
                if (cmd == null)
                {
                    // 重连后可能会出现
                    efem.OnUnhandledResponse(msg_param);
                    return;
                }
                cmd.AddHistory(this.ToString(), msg_param, ref timestamp);
                cmd.Response = msg_param;
                this.OnSetResultOrError(cmd);

                // 切记放在最后赋值。该变量在多线程中作为判据使用。
                cmd.State = this.AckFinished(cmd) ? CommandStatus.Succeed : status;
            }

            // 子类重写，实现消息解释，并给结果
            protected virtual void OnSetResultOrError(CommandState cs) { }

            // 只有 ACK 才会根据 cs 可能返回 true。返回 true 表示消息链完成。
            protected virtual bool AckFinished(CommandState cs) => false;

            bool Contains(CommandState cs)
            {
                if (cs.MsgParam.Length > this.msgParam.Length)
                {
                    return cs.MsgParam.StartsWith(this.msgParam);
                }
                else
                {
                    return this.msgParam.StartsWith(cs.MsgParam);
                }
            }
        }

        // 收到 EVT 消息,触发事件即可
        class EvtAnalyser : BaseAnalyser
        {
            Dictionary<string, MsgAnalyser> msgAnalyses;

            public EvtAnalyser(Efem efem)
            {
                msgAnalyses = new Dictionary<string, MsgAnalyser>() {
                    { "MAPDT" , new MapdtAnalyser() },
                    { "STATE", new StateAnalyser() },
                    { "TRANSREQ", new TransreqAnalyser() },
                    { "SIGSTAT", new SigstatAnalyser(efem) },
                    { "CSTID",new CstidAnalyser() },
                    { "WID",new WidAnalyser() },
                };
            }

            public override void Analysis(Efem efem, string msg_param, ref DateTime timestamp)
            {
                string[] ss = msg_param.Split('/');
                msgAnalyses[ss[0]].Analysis(efem, msg_param, ss);
            }

            abstract class MsgAnalyser
            {
                public abstract void Analysis(Efem efem, string msg_param, params string[] ss);
            }
            class MapdtAnalyser : MsgAnalyser
            {
                public override void Analysis(Efem efem, string msg_param, params string[] ss)
                {
                    efem.OnMapdtReaded(ss[1], ss[2]);
                }
            }
            class StateAnalyser : MsgAnalyser
            {
                public override void Analysis(Efem efem, string msg_param, params string[] ss)
                {
                    switch (ss[1][0])
                    {
                        case 'V':   // VER
                            efem.OnVersionReceived(ss[2]);
                            break;
                        case 'T':   // TRACK
                            efem.OnTrackStateReaded(ss[2]);
                            break;
                        case 'D':
                        case 'P':
                            // DPRS
                            efem.OnPressureReaded(ss[1], ss[2]);
                            break;
                        case 'F':   // FFU
                            efem.OnFanSpeedReaded(ss[1], ss[2]);
                            break;
                        case 'A':   // ALIGNMENT
                            efem.OnAlignmengChanged(ss[1], ss[2]);
                            break;
                        default:
                            efem.OnLogUnknownKeyword("StateAnalyser.{0} {1} 未知的 STATE 类型，参数：" + msg_param);
                            break;
                    }
                }
            }
            class TransreqAnalyser : MsgAnalyser
            {
                public override void Analysis(Efem efem, string msg_param, params string[] ss)
                {
                    Enum.TryParse<RequestType>(ss[2], out RequestType type);
                    efem.OnRequestChanged(ss[1], type);
                }
            }
            class SigstatAnalyser : MsgAnalyser
            {
                Dictionary<char, Action<string, int, int>> actions;

                public SigstatAnalyser(Efem efem)
                {
                    this.actions = new Dictionary<char, Action<string, int, int>>()
                    {
                        {'S', efem.OnSystemIOChanged },
                        {'P', efem.OnLoadPortIOChanged },
                    };
                }

                public override void Analysis(Efem efem, string msg_param, params string[] ss)
                {
                    int data1 = Convert.ToInt32(ss[2], 16);
                    int data2 = Convert.ToInt32(ss[3], 16);
                    this.actions[ss[1][0]](ss[1], data1, data2);
                }
            }
            class CstidAnalyser : MsgAnalyser
            {
                public override void Analysis(Efem efem, string msg_param, params string[] ss)
                {
                    efem.OnCarrierIdReaded(ss[1], ss[2]);
                }
            }
            class WidAnalyser : MsgAnalyser
            {
                public override void Analysis(Efem efem, string msg_param, params string[] ss)
                {
                    efem.OnWidRecipeReaded(ss[1], ss[2], msg_param.Substring(msg_param.LastIndexOf('/') + 1));
                }
            }
        }

        // 收到 INF 消息，意味着操作结束
        class InfAnalyser : BaseAnalyser
        {
            public override void Analysis(Efem efem, string msg_param, ref DateTime timestamp)
            {
                base.msgParam = msg_param;
                efem.InnerSendMessage("ACK", msg_param);
                if (msg_param[0] == 'R')
                {
                    // INF 消息只有 READY
                    return;
                }

                if (msg_param == "EMS")
                {
                    efem.flag_ems = true;
                }

                base.SetResult(efem, msg_param, CommandStatus.Succeed, ref timestamp);
            }

            protected override void OnSetResultOrError(CommandState cs) => cs.Analyser.Analysis(cs);

            public override string ToString() => "INF";
        }

        // 收到 ABS 消息 --- 异常中止
        class AbsAnalyser : BaseAnalyser
        {
            public override void Analysis(Efem efem, string msg_param, ref DateTime timestamp)
            {
                base.msgParam = msg_param.Substring(0, msg_param.IndexOf('|'));
                base.SetResult(efem, msg_param, CommandStatus.ABS, ref timestamp);
            }

            protected override void OnSetResultOrError(CommandState cs)
            {
                cs.Result = cs.Response.Substring(cs.Response.IndexOf('|') + 1);
                cs.Error = null;
            }

            public override string ToString() => "ABS";
        }

        // 收到 CAN 消息 --- 操作当前不能执行
        class CanAnalyser : BaseAnalyser
        {
            public override void Analysis(Efem efem, string msg_param, ref DateTime timestamp)
            {
                base.msgParam = msg_param.Substring(0, msg_param.IndexOf('|'));
                base.SetResult(efem, msg_param, CommandStatus.CAN, ref timestamp);
            }

            protected override void OnSetResultOrError(CommandState cs)
            {
                cs.Result = cs.Response.Substring(cs.Response.IndexOf('|') + 1);
                cs.Error = null;
            }

            public override string ToString() => "CAN";
        }

        // 收到 NAK 消息 --- 未知命令.不应该出现,说明代码有问题,没有检查参数有效性
        class NakAnalyser : BaseAnalyser
        {
            public override void Analysis(Efem efem, string msg_param, ref DateTime timestamp)
            {
                base.msgParam = msg_param.Substring(msg_param.IndexOf('|') + 1);
                base.SetResult(efem, msg_param, CommandStatus.NAK, ref timestamp);
            }

            protected override void OnSetResultOrError(CommandState cs)
            {
                cs.Result = cs.Response.Substring(cs.Response.IndexOf('|') + 1);
                // 不需要解释，参数 或 消息不对
            }

            public override string ToString() => "NAK";
        }

        // 收到 ACK 消息
        class AckAnalyser : BaseAnalyser
        {
            protected override bool AckFinished(CommandState cs) => cs.AckSucced;

            protected override void OnSetResultOrError(CommandState cs)
            {
                if (cs.AckSucced)
                {
                    cs.Result = cs.Response;
                }
            }

            public override string ToString() => "ACK";
        }

        #endregion
    }

    class CommandState
    {
        List<MessageLog> response;

        public long Tag;                        // 发送指令时的时间戳
        public string Type;                     // 指令类型：MOV、SET、GET
        public string MsgParam;                 // 指令参数，包括类型及参数

        public CommandStatus State;             // 指令执行状态
        public string Response;                 // 指令执行结果，最后一条回复
        public string Result;                   // 指令执行结果。回复中的重要内容
        public object Result2;                  // INF 将 Result 结果解析成对应指令的数据
        public BaseGetAnalyser Analyser;        // Result2 解析类
        public string Error;                    // 出现异常时的解释说明
        public bool AckSucced;                  // 消息链到 ACK 就结束的命令，赋值 true

        public CommandState(long tag, string type, string msg_para, BaseGetAnalyser analyser, bool ackSucced)
        {
            this.response = new List<MessageLog>();
            this.Tag = tag;
            this.Type = type;
            this.MsgParam = msg_para;
            this.Analyser = analyser;
            this.AckSucced = ackSucced;

            this.response.Add(new MessageLog() { Message = string.Concat(this.Type, ":", this.MsgParam), Timestamp = DateTime.Now });
        }

        // 记录 EFEM 的回复
        public void AddHistory(string type, string msg_para, ref DateTime timestamp)
        {
            this.response.Add(new MessageLog() { Message = string.Concat(type, ":", msg_para), Timestamp = timestamp });
        }

        public MessageLog[] GetFullMessages() => this.response.ToArray();
    }

    /// <summary>
    /// 指令执行结果
    /// </summary>
    class ExecuteResult
    {
        readonly Efem efem;
        readonly long tag;
        CommandStatus state;
        string response;        // 最后一条回复
        string result;          // 指令执行结果。回复中的重要内容
        object result2;         // INF 对 result 的解析
        string error;           // 出现异常时的解释说明

        internal ExecuteResult(Efem efem, long tag)
        {
            this.efem = efem;
            this.tag = tag;
        }

        /// <summary>
        /// 指令执行状态
        /// </summary>
        public CommandStatus State => this.UpdateState();

        /// <summary>
        /// 指令最后一条回复
        /// </summary>
        public string Response => this.response;

        /// <summary>
        /// 最后一条回复中的重要内容
        /// </summary>
        public string Result => this.result;

        /// <summary>
        /// 将 <seealso cref="Result"/> 解析成对应指令（如 WID）的结果。
        /// </summary>
        public object Result2 => this.result2;

        /// <summary>
        /// 异常说明。NAK、ABS、CAN
        /// </summary>
        public string Error => this.error;

        /// <summary>
        /// 第一条为发送的指令，剩下的为收到的回复
        /// </summary>
        public MessageLog[] FullMessages { get; private set; }

        CommandStatus UpdateState()
        {
            if (this.state != CommandStatus.Executing)
            {
                // 已经执行完毕
                return this.state;
            }

            var cs = this.efem.GetCommandState(this.tag, out CommandStatus state);
            if (state != CommandStatus.Executing)
            {
                // 已经执行完毕
                this.response = cs.Response;
                this.result = cs.Result;
                this.error = cs.Error;
                this.result2 = cs.Result2;
                this.FullMessages = cs.GetFullMessages();
            }
            return this.state = state;
        }
    }

    [System.Diagnostics.DebuggerDisplay("{Timestamp}\t{Message}")]
    class MessageLog
    {
        public DateTime Timestamp;
        public string Message;
    }

    #region INF 结果解析

    // 解析基类，适用于大部分 GET，子类按协议文档中的顺序定义，便于维护。可以拆分到不同的文件中。
    class BaseGetAnalyser
    {
        /// <summary>
        /// 解析返回值。Response 内容为：后面的内容
        /// </summary>
        /// <param name="state">指令执行信息，根据 <see cref="CommandState.Response"/> 解析出 Result、Result2</param>
        public virtual void Analysis(CommandState state)
        {
            state.Result2 = state.Result = state.Response.Substring(state.Response.LastIndexOf('/') + 1);
        }
    }

    class GetMapdtAnalyser : BaseGetAnalyser
    {
        public override void Analysis(CommandState state)
        {
            // INF:MATDP/P1/111111111111
            // INF:MATDP/BF1/111111111111
            int idx = state.Response[6] == 'P' ? 9 : 10;
            state.Result = state.Response.Substring(idx);
            state.Result2 = ConvertFromString(state.Result);
        }

        public static SubstrateStatus[] ConvertFromString(string result)
        {
            //int len = result.Length;
            //SubstrateStatus[] ss = new SubstrateStatus[len];
            //for (int i = 0; i < len; i++)
            //{
            //    ss[i] = (SubstrateStatus)(result[i] - '0');
            //}
            //return ss;
            return (from i in result select (SubstrateStatus)(i - '0')).ToArray();
        }
    }

    class GetWidAnalyser : BaseGetAnalyser
    {
        public override void Analysis(CommandState state)
        {
            //INF:WID/ALIGN1/WIDRecipe100/READFAIL,0,".\ImageLog\20210604153226_406_001__NG.bmp";
            //INF:WID/ALIGN1/WIDRecipe100/READFAIL;
            base.Analysis(state);
            state.Result2 = ConvertFromString(state.Result);
        }

        public static string[] ConvertFromString(string result)
        {
            return result.Split(',');
        }
    }

    class GetErrorAnalyser : BaseGetAnalyser
    {
        public override void Analysis(CommandState state)
        {
            // INF:ERROR/NOTHING
            // INF:ERROR/CODE/WHERE
            state.Result = state.Response.Substring(6);
            state.Result2 = ConvertFromString(state.Result);
        }

        public static string ConvertFromString(string result)
        {
            return result;
        }
    }

    class GetTrackAnalyser : BaseGetAnalyser
    {
        static Type stateType = typeof(TrackStatus);

        public override void Analysis(CommandState state)
        {
            // INF:STATE/TRACK/[a/b/c/d|[abcd]]
            state.Result = state.Response.Substring(12);
            state.Result2 = ConvertFromString(state.Result);
        }

        public static TrackStatus[] ConvertFromString(string result)
        {
            return result.Contains('/') ? GetStatuses1(result) : GetStatuses2(result);
        }
        static TrackStatus[] GetStatuses1(string result)
        {
            //string[] ss = result.Split('/');
            //TrackStatus[] ts = new TrackStatus[ss.Length];
            //for (int i = 0; i < ss.Length; i++)
            //{
            //    ts[i] = ss[i][0] == '?' ? TrackStatus.Unknown : (TrackStatus)Enum.Parse(stateType, "S" + ss[i]);
            //}

            //return ts;

            return (from i in result.Split('/') select FromString(i)).ToArray();
        }
        static TrackStatus FromString(string value)
        {
            return value[0] == '?' ? TrackStatus.Unknown : (TrackStatus)Enum.Parse(stateType, "S" + value);
        }
        static TrackStatus[] GetStatuses2(string result)
        {
            //TrackStatus[] ts = new TrackStatus[result.Length];
            //for (int i = 0; i < result.Length; i++)
            //{
            //    ts[i] = result[i] == '?' ? TrackStatus.Unknown : (TrackStatus)(result[i] - '0');
            //}

            //return ts;

            return (from i in result select FromChar(i)).ToArray();
        }
        static TrackStatus FromChar(char c)
        {
            return c == '?' ? TrackStatus.Unknown : (TrackStatus)(c - '0');
        }
    }

    class GetSensorAnalyser : BaseGetAnalyser
    {
        public override void Analysis(CommandState state)
        {
            // INF:STATE/DPRS1/[SNO1|xxxx,SNO2|,yyyy...]
            // INF:STATE/FFU1/[SNO1|xxxx,SNO2|,yyyy...]

            state.Result = state.Response.Substring(state.Response.IndexOf('/', 7) + 1);
            state.Result2 = ConvertFromString(state.Result);
        }

        public static string[][] ConvertFromString(string result)
        {
            //string[] gs = result.Split(',');
            //string[,] gr = new string[gs.Length, 2];
            //string[] tmp;

            //for (int i = 0; i < gs.Length; i++)
            //{
            //    tmp = gs[i].Split('|');
            //    gr[i, 0] = tmp[0];
            //    gr[i, 1] = tmp[1];
            //}

            //return gr;

            return (from g in result.Split(',') select (from i in g.Split('|') select i).ToArray()).ToArray();
        }
    }

    class GetAlignmentAnalyser : BaseGetAnalyser
    {
        public override void Analysis(CommandState state)
        {
            // INF:STATE/ALIGNMENT/R,X,Y
            state.Result = state.Response.Substring(16);
            state.Result2 = ConvertFromString(state.Result);
        }

        public static object[] ConvertFromString(string result)
        {
            return result.Split(',');
        }
    }

    // CLAMP、MODE、TRANSREQ 不需要，Result 即可表示

    class GetSigStateAnalyser : BaseGetAnalyser
    {
        public override void Analysis(CommandState state)
        {
            // INF:SIGSTAT/P1/00000120/00000F01
            // INF:SIGSTAT/SYSTEM/00000120/00000F01

            state.Result = state.Response.Substring(state.Response.IndexOf('/', 10) + 1);
            state.Result2 = (from i in state.Result.Split('/') select Convert.ToInt32(i, 16)).ToArray();
        }
    }

    // EVENT、CSTID、PURGE、SIZE 不需要，Result 即可表示

    #region INF 结果解析所需类型

    /// <summary>
    /// 片盒每层状态
    /// </summary>
    enum SubstrateStatus
    {
        None,
        NormalSubstrate,
        ThickSubstrate,
        CrossSlotteSubstrate,
        BowSubstrrate,
        MultipleSubstrates,
        ThinSubstrate,
        Others
    }

    /// <summary>
    /// 不同位置晶圆情况
    /// </summary>
    enum TrackStatus
    {
        Unknown = -1,
        SNONE,
        Exist,
        S150,
        S200,
        S300,
    }

    #endregion

    #endregion

    #region 函数形参 - 枚举

    public enum CommandStatus
    {
        Executing,
        Succeed,
        CAN,
        ABS,
        NAK,
        EMS,
        TcpError,
    }

    enum Arms
    {
        ARM1,
        ARM2,
        //ARM3,
    }

    enum HeightPosition
    {
        UP,
        DOWN,
    }

    [Flags]
    enum EfemStages
    {
        Align = 1,
        Stage = 2,
        LoadPort = 4,
        BufferPort = 8,
    }

    enum Stages
    {
        LLA,
        LLB,
        LLC,
        LLD,
        //LLE,
        //LLF,
    }

    enum SwitchState
    {
        ON,
        OFF,
    }

    enum E84Mode
    {
        AUTO,
        MANUAL,
    }

    enum RequestType
    {
        LOAD,
        UNLOAD,
        STOP,
    }

    enum Towers
    {
        RED,
        YELLOW,
        GREEN,
        BLUE,
        WHITE,
        BUZZER1,
        BUZZER2,
    }

    enum Indicator
    {
        LOAD,
        UNLOAD,
        MANUAL,
        ACCESS,

        AUTO,
        CLAMP,
        DOCK,
        ALARM,
    }

    enum LightStatus
    {
        ON,
        OFF,
        BLINK,
    }

    enum EventTypes
    {
        ALL,
        MAPDT,
        TRANSREQ,
        SYSTEM,
        PORT,
        CSTID,
        WID,
        PRS,
        FFU,
    }

    [Flags]
    enum SizeMode
    {
        S150 = 1,
        S200 = 2,
        S300 = 4,
        SNONE = 8,
    }

    #region IO 枚举 2022.1.20

    //[Flags]
    //enum SystemInputIO
    //{
    //    None,

    //    VacSrcPressure1             = 1,
    //    VacSrcPressure2             = 2,
    //    CompAirPressure1            = 4,
    //    CompAirPressure2            = 8,
    //    DiffPressSensor1            = 16,
    //    DiffPressSensor2            = 32,
    //    FfuNormal                   = 64,
    //    IonizerNormal               = 128,
    //    RunMode                     = 256,
    //    DrivePower                  = 512,
    //    DoorClosed                  = 1024,
    //    AreaSensor_BarInterLock     = 2048,
    //}

    //[Flags]
    //enum LoadPortInputIO
    //{
    //    None,

    //    CarrierPlaced           = 1,
    //    CarrierPresence         = 2,
    //    AccessSWPressed         = 4,
    //    CarrierLocked           = 8,

    //    CoverClosed             = 16,
    //    CoverLocked             = 32,
    //    DoorOpened              = 128,

    //    InfoPadAPressed         = 256,
    //    InfoPadBPressed         = 512,
    //    InfoPadCPressed         = 1024,
    //    InfoPadDPressed         = 2048,

    //    AdapterPresence         = 0x1000,
    //    AdapterConnected        = 0x2000,
    //    AdapterValid            = 0x4000,

    //    N2PurgeProcessing       = 0x10000,

    //    E84_Valid               = 0x1000000,
    //    E84_CS0                 = 0x2000000,
    //    E84_CS1                 = 0x4000000,
    //    E84_Resv                = 0x8000000,
    //    E84_TR_REQ              = 0x10000000,
    //    E84_Busy                = 0x20000000,
    //    E84_Compt               = 0x40000000,
    //    E84_Cont                = -2147483648,
    //}

    //[Flags]
    //enum SystemOutputIO
    //{
    //    None,

    //    RedON               = 1,
    //    YellowON            = 2,
    //    GreenON             = 4,
    //    BlueON              = 8,
    //    WhiteON             = 16,
    //    RedFlash            = 32,
    //    YellowFlash         = 64,
    //    GreenFlash          = 128,
    //    BlueFlash           = 256,
    //    WhiteFlash          = 512,
    //    Buzzer1             = 1024,
    //    Buzzer2             = 2048,
    //}

    //[Flags]
    //enum LoadPortOutputIO
    //{
    //    None,

    //    Presence            = 1,
    //    Placement           = 2,
    //    Load                = 4,
    //    Unload              = 8,

    //    ManualMode          = 16,
    //    Error               = 32,
    //    Clamp               = 64,
    //    Dock                = 128,
    //    AccessSW            = 256,

    //    E84_L_REQ           = 0x1000000,
    //    E84_U_REQ           = 0x2000000,
    //    E84_Resv1           = 0x4000000,
    //    E84_Ready           = 0x8000000,
    //    E84_Resv2           = 0x10000000,
    //    E84_Resv3           = 0x20000000,
    //    E84_HO_AVBL         = 0x40000000,
    //    E84_ES              = -2147483648,
    //}

    #endregion

    #endregion

    static class CheckUtil
    {
        /// <summary>
        /// 检查枚举有效性：是否有定义
        /// </summary>
        /// <typeparam name="T">枚举类型</typeparam>
        /// <param name="value">枚举值</param>
        /// <param name="line">代码行。不允许传值</param>
        public static void CheckEnum<T>(ref T value, [CallerLineNumber] int line = 0) where T : Enum
        {
            char c = value.ToString()[0];
            if (char.IsNumber(c) || c == '-')
            {
                // 枚举是数字值，即不在定义范围内
                throw new ArgumentOutOfRangeException($"枚举类型【{typeof(T).Name}】枚举值无效！代码行：{line}");
            }
        }

        /// <summary>
        /// 检查枚举值有效性：不为 None，即值不为 0
        /// </summary>
        /// <typeparam name="T">枚举类型</typeparam>
        /// <param name="value">枚举值</param>
        /// <param name="line">代码行。不允许传值</param>
        public static void CheckEnumNotNone<T>(ref T value, [CallerLineNumber] int line = 0) where T : Enum
        {
            CheckEnum(ref value, line);
            if (Convert.ToInt32(value) == 0)
            {
                throw new ArgumentOutOfRangeException($"枚举值不能为 None 或 0！代码行：{line}");
            }
        }

        /// <summary>
        /// 检查被 Flag 标记的枚举类型枚举值有效性：是否定义，是否有无效值
        /// </summary>
        /// <typeparam name="T">枚举类型</typeparam>
        /// <param name="value">枚举值</param>
        /// <param name="invalid">无效枚举值</param>
        /// <param name="line">代码行。不允许传值</param>
        public static void CheckEnumFlag<T>(ref T value, T invalid, [CallerLineNumber] int line = 0) where T : Enum
        {
            CheckEnum(ref value, line);
            int v = Convert.ToInt32(value);
            int i = Convert.ToInt32(invalid);
            if ((v & i) > 0)
            {
                throw new ArgumentOutOfRangeException($"传入的枚举值中有无效值！代码行：{line}");
            }
        }

        public static void CheckTcpClient(TcpClient client, [CallerLineNumber] int line = 0)
        {
            if (client == null || !client.Connected)
            {
                throw new InvalidOperationException($"TCP 连接已断开！代码行：{line}");
            }
        }

    }
}
